package activityChecker

import (
	"errors"
	"time"
)

/*
该struct的func不是goroutine安全的，请自行处理同步

根据使用场景，提供精确和粗略的两种统计方式，
精确但需要更多计算，适合不频繁的统计，
粗略但执行更快，适合高频率的统计
*/
type ActivityChecker struct {
	name           string
	lastAccessTime time.Time
	activity       int

	decayPeriod       time.Duration
	maxActivity       int
	doubleMaxActivity int
}

func NewActivityChecker(name string, decayPeriod time.Duration, maxActivity int) *ActivityChecker {
	if int64(decayPeriod) < 0 {
		panic(errors.New(name + ": decayPeriod is <= 0 !"))
	}
	if maxActivity <= 0 {
		panic(errors.New(name + ": maxActivity is <= 0 !"))
	}
	return &ActivityChecker{
		name:              name,
		lastAccessTime:    time.Now(),
		decayPeriod:       decayPeriod,
		maxActivity:       maxActivity,
		doubleMaxActivity: maxActivity * 2,
	}
}

//根据调用时的时间与上一次调用的时间的间隔，计算活跃度衰减，减小活跃度
func (self *ActivityChecker) decay() {
	now := time.Now()
	duration := now.Sub(self.lastAccessTime)

	decayPercent := float64(duration.Nanoseconds()) / float64(self.decayPeriod.Nanoseconds())
	if decayPercent < 0 {
		decayPercent = 0
	} else if decayPercent > 1.0 {
		decayPercent = 1.0
	}

	self.changeActivity(int(-decayPercent*float64(self.maxActivity)), self.maxActivity)

	self.lastAccessTime = now
}

func (self *ActivityChecker) changeActivity(delta int, max int) {
	self.activity += delta
	if self.activity < 0 {
		self.activity = 0
	} else if self.activity > max {
		self.activity = max
	}
}

func (self *ActivityChecker) Name() string {
	return self.name
}

///////////////////////////
//对于访问不十分频繁的情况，在每次的Access和Activity调用时，都先做一次Decay，保证活跃度的精确性
func (self *ActivityChecker) AccessPrecise(count int) {
	self.decay()
	self.changeActivity(count, self.maxActivity)
}
func (self *ActivityChecker) ActivityPrecise() int {
	self.decay()
	return self.activity
}

////////////////////////////
//在高访问的情况下，考虑到对执行效率的影响，建议隔一段时间做一次Decay，而不是每次Access或Activity前Decay，在效率和精确性上有一个平衡
// 按照每周期一次decay来计算，允许最大累计到2倍最大活跃度，这样当满活跃度的情况下做一周期的decay的时候，能够保证满活跃，而不是减到0
func (self *ActivityChecker) DecayRough() {
	self.decay()
}
func (self *ActivityChecker) AccessRough(count int) {
	self.changeActivity(count, self.doubleMaxActivity)
}
func (self *ActivityChecker) ActivityRough() int {
	if self.activity < 0 {
		return 0
	} else if self.activity > self.maxActivity {
		return self.maxActivity
	} else {
		return self.activity
	}
}
